package com.cloudft.http.services;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.zip.GZIPInputStream;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import android.util.Log;

import com.cloudft.http.models.CFHttpException;
import com.cloudft.http.models.HTTPRequest;
import com.cloudft.http.models.HTTPResponse;

/**
 * HTTPService class- This class makes the server connection and fetches the
 * latest data from the server. It also created the HTTPResponse objects and set
 * its parameters
 * */

public class HTTPService {

	public HTTPResponse fetchServerData(HTTPRequest request)
			throws CFHttpException {

		HTTPResponse response = null;

		// Create the default HTTP client
		DefaultHttpClient httpClient = new DefaultHttpClient();

		try {

			// Get the http response from the server
			HttpResponse httpResponse = null;

			if (request.getRequestType() != null
					&& request.getRequestType().trim().equalsIgnoreCase("post")) {
				// Handle post request
				HttpPost httpRequest = new HttpPost(request.getUrl());
				
				//GZip option is enabled by default. User can disable it if he wants
				if(request.getGzipDisabled() == null || !request.getGzipDisabled()){
					httpRequest.addHeader("Accept-Encoding", "gzip");
				}
				
				if (request.getPostParams() != null) {
					httpRequest.setEntity(new UrlEncodedFormEntity(request
							.getPostParams()));
				}
				httpResponse = httpClient.execute(httpRequest);

			} else {
				// Handle get request
				HttpRequestBase httpRequest = new HttpGet(request.getUrl());
			
				//GZip option is enabled by default. User can disable it if he wants
				if(request.getGzipDisabled() == null || !request.getGzipDisabled()){
					httpRequest.addHeader("Accept-Encoding", "gzip");
				}
				
				httpResponse = httpClient.execute(httpRequest);
			}

			// check http response code, if not OK (HTTP 200) then throw
			// exception
			if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
				throw new CFHttpException(httpResponse.getStatusLine()
						.getStatusCode(), httpResponse.getStatusLine()
						.getReasonPhrase());

			String strResponse = "";
			Header contentEncoding = httpResponse.getFirstHeader("Content-Encoding");
			//Read the zipped response if content-encoding is set to be 'gzip'
			if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
				InputStream instream = httpResponse.getEntity().getContent();
			    instream = new GZIPInputStream(instream);
			    strResponse = convertinputStreamToString(instream);
			}else{
				strResponse = EntityUtils.toString(httpResponse.getEntity());
			}

			// Set response in HTTPResponse object
			response = new HTTPResponse();
			response.setResponse(strResponse);
			response.setUrl(request.getUrl());
			response.setFromCache(false);

			// Set timestamp value
			Date now = new Date();
			response.setTimeStamp(now.getTime());

			if (request.getIsCacheable()) {
				setTimeToLive(httpResponse, response);
			}

		} catch (ClientProtocolException e) {
			Log.i("HTTPService ClientProtocolException :", e.getMessage());
		} catch (UnknownHostException e) {
			CFHttpException cfHttpException = new CFHttpException();
			cfHttpException.setMessage("UnknownHostException: " + e.getMessage());
			throw cfHttpException;
		} catch (IOException e) {
			Log.i("HTTPService IOException :", e.getMessage());
		}

		return response;
	}

	/**
	 * setTimeToLive() - Reads the max-age from cache control header and set the timeToLive
	 * */
	private void setTimeToLive(HttpResponse httpResponse, HTTPResponse response) {
		// Read cache control header and set the max age
		if (httpResponse.containsHeader("Cache-Control")) {
			String cacheControl = httpResponse.getFirstHeader("Cache-Control")
					.getValue();
			String[] splitCommas = cacheControl.split(",");

			boolean isFound = false;

			for (String pair : splitCommas) {
				if (pair.contains("max-age")) {
					String[] values = pair.split("=");
					response.setTimeToLive(new Long(values[1]));
					isFound = true;
					break;
				}
			}

			// We first look for the max-age in the cache control header, if it is not present, we read
			// the expiration date and set the timeToLive
			if (!isFound) {
				readExpirationHeaders(httpResponse, response);
			}
		}
	}

	/**
	 * readExpirationHeaders() - Compares the expiration date and set the timeToLive
	 * */
	private void readExpirationHeaders(HttpResponse httpResponse, HTTPResponse response) {
		Date date = null;
		Date expirationDate = null;

		// Reading the expiration date and compare them with the date to set the timeToLive
		if (httpResponse.containsHeader("Expires")) {
			String strExpirationDate = httpResponse.getFirstHeader("Expires")
					.getValue();
			DateFormat dateFormat = DateFormat.getDateInstance();
			try {
				expirationDate = dateFormat.parse(strExpirationDate);
			} catch (ParseException e) {
				Log.i("ParseException in evaluateMaxAge(Expires) ",
						e.getMessage());
			}

			if (httpResponse.containsHeader("Date")) {
				String strDate = httpResponse.getFirstHeader("Date").getValue();
				try {
					date = dateFormat.parse(strDate);
				} catch (ParseException e) {
					Log.i("ParseException in evaluateMaxAge(Date) ",
							e.getMessage());
				}
			}
		}

		if (date != null && expirationDate != null && expirationDate.compareTo(date) > 0) {
			response.setTimeToLive(expirationDate.getTime() - date.getTime());
		}
	}
	
	/**
	 * 
	 * Helper method that converts the input stream into string with UTF-8 encoding
	 * @param ists
	 * @return string response
	 * @throws IOException
	 */
	private String convertinputStreamToString(InputStream ists) throws IOException {
	       
        if (ists != null) {
            StringBuilder sb = new StringBuilder();
            String line;
 
            try {
                BufferedReader r1 = new BufferedReader(new InputStreamReader(ists, "UTF-8"));
                while ((line = r1.readLine()) != null) {
                    sb.append(line).append("\n");
                }
            } finally {
                ists.close();
            }
            return sb.toString();
        } else {       
            return "";
        }
    }

}
